#lang racket
(require rackunit
         racket/contract/private/arrow-common
         
         (for-template racket/contract/private/arrow-val-first)
         racket/contract/private/application-arity-checking
         racket/contract/private/arr-i-parse
         racket/contract/private/merge-cache)

(check-equal? (matches-arity-exactly? (λ () 1) 0 0 '() '()) #t)
(check-equal? (matches-arity-exactly? (λ () 1) 1 1 '() '()) #f)
(check-equal? (matches-arity-exactly? (λ () 1) 0 1 '() '()) #f)
(check-equal? (matches-arity-exactly? (λ () 1) 0 #f '() '()) #f)
(check-equal? (matches-arity-exactly? (λ (x y) x) 2 2 '() '()) #t)
(check-equal? (matches-arity-exactly? (λ (x y) x) 1 1 '() '()) #f)
(check-equal? (matches-arity-exactly? (λ (x y) x) 2 3 '() '()) #f)
(check-equal? (matches-arity-exactly? (λ (x y) x) 3 #f '() '()) #f)

(check-equal? (matches-arity-exactly? (case-lambda [() 1] [(x) 2])
                                      0 1 '() '())
              #t)
(check-equal? (matches-arity-exactly? (case-lambda [() 1] [(x) 2])
                                      0 2 '() '())
              #f)
(check-equal? (matches-arity-exactly? (case-lambda [() 1] [(x y) 2])
                                      0 2 '() '())
              #f)
(check-equal? (matches-arity-exactly? (case-lambda [() 1] [(x y) 2])
                                      0 1 '() '())
              #f)
(check-equal? (matches-arity-exactly? (case-lambda [() 1] [(x y) 2])
                                      0 #f '() '())
              #f)

(check-equal? (matches-arity-exactly? (lambda (x . y) x)
                                      1 #f '() '())
              #t)
(check-equal? (matches-arity-exactly? (lambda (x . y) x)
                                      0 #f '() '())
              #f)
(check-equal? (matches-arity-exactly? (lambda (x #:y y) y)
                                      1 1 '(#:y) '())
              #t)
(check-equal? (matches-arity-exactly? (lambda (x #:y y #:z z) y)
	                              1 1 '(#:y #:z) '())
              #t)
(check-equal? (matches-arity-exactly? (lambda (x #:y y #:z z) y)
	                              1 1 '(#:y) '())
              #f)
(check-equal? (matches-arity-exactly? (lambda (x #:y y #:z z) y)
	                              1 1 '(#:z) '())
              #f)
(check-equal? (matches-arity-exactly? (lambda (x #:y y #:z z) y)
	                              1 1 '() '())
              #f)
(check-equal? (matches-arity-exactly? (lambda (x #:y y #:z z) y)
	                              1 1 '() '(#:x))
              #f)
(check-equal? (matches-arity-exactly? (lambda (x #:y y #:z [z 1]) y)
	                              1 1 '(#:y) '(#:z))
              #t)
(check-equal? (matches-arity-exactly? (lambda (x #:y y #:z [z 1]) y)
	                              1 1 '(#:y) '())
              #f)
(check-equal? (matches-arity-exactly? (lambda (x #:y y #:z [z 1]) y)
	                              1 1 '() '(#:z))
              #f)
(check-equal? (matches-arity-exactly? (lambda (x #:y y #:z [z 1]) y)
	                              1 1 '(#:y #:z) '())
              #f)
(check-equal? (matches-arity-exactly? (lambda (x #:y y #:z [z 1]) y)
	                              1 1 '() '(#:y #:z))
              #f)

(define (->-shapes arg)
  (define-values (a b) (->-valid-app-shapes arg))
  a)
(check-equal? (->-shapes #'(-> integer? integer?))
              (valid-app-shapes '(1) '() '()))
(check-equal? (->-shapes #'(-> integer? boolean? integer?))
              (valid-app-shapes '(2) '() '()))
(check-equal? (->-shapes #'(-> integer? #:x any/c integer?))
              (valid-app-shapes '(1) '(#:x) '()))
(check-equal? (->-shapes #'(-> integer? (... ...) any))
              (valid-app-shapes 0 '() '()))
(check-equal? (->-shapes #'(-> integer? integer? (... ...) any))
              (valid-app-shapes 1 '() '()))
(check-equal? (->-shapes #'(-> integer? integer? (... ...) integer? any))
              (valid-app-shapes 2 '() '()))
(check-equal? (->-shapes #'(-> integer? integer? (... ...) integer? boolean? char? any))
              (valid-app-shapes 4 '() '()))
(check-equal? (->-shapes #'(-> integer? boolean? char? (... ...) integer? char? any))
              (valid-app-shapes 4 '() '()))

(define (->*-shapes arg)
  (define-values (a b) (->*-valid-app-shapes arg))
  a)
(check-equal? (->*-shapes #'(->* (integer? #:x any/c #:y any/c) integer?))
              (valid-app-shapes '(1) '(#:x #:y) '()))
(check-equal? (->*-shapes #'(->* () (integer? #:x any/c #:y any/c) integer?))
              (valid-app-shapes '(0 1) '() '(#:x #:y)))
(check-equal? (->*-shapes #'(->* (any/c) (any/c) #:rest any/c integer?))
              (valid-app-shapes '(1 2 . 3) '() '()))

(check-true  (valid-argument-list? #'(f x) (valid-app-shapes '(1 2 . 3) '() '())))
(check-true  (valid-argument-list? #'(f x y) (valid-app-shapes '(1 2 . 3) '() '())))
(check-true  (valid-argument-list? #'(f x y a b c d) (valid-app-shapes '(1 2 . 3) '() '())))
(check-false (valid-argument-list? #'(f) (valid-app-shapes '(1 2 . 3) '() '()) #f))
(check-true  (valid-argument-list? #'(f #:x x) (valid-app-shapes '(0) '(#:x) '())))
(check-true  (valid-argument-list? #'(f #:x x) (valid-app-shapes '(0) '() '(#:x))))
(check-true  (valid-argument-list? #'(f) (valid-app-shapes '(0) '() '(#:x))))
(check-false (valid-argument-list? #'(f) (valid-app-shapes '(0) '(#:x) '()) #f))
(check-false (valid-argument-list? #'(f #:y y) (valid-app-shapes '(0) '(#:x) '()) #f))
(check-false (valid-argument-list? #'(f #:x) (valid-app-shapes '(0) '(#:x) '()) #f))

(define/merge-cache (f x y z w)
  (list x y z w))

(check-equal? (f 1 2 3 4) (list 1 2 3 4))
(check-equal? (f 1 2 3 4) (list 1 2 3 4))
(check-equal? (f 1 2 3 0) (list 1 2 3 0))
(check-equal? (f 1 2 3 0) (list 1 2 3 0))
(check-equal? (f 1 2 0 4) (list 1 2 0 4))
(check-equal? (f 1 0 3 4) (list 1 0 3 4))
(check-equal? (f 1 0 3 4) (list 1 0 3 4))
(check-equal? (f 0 2 3 4) (list 0 2 3 4))
(check-equal? (f 0 2 3 4) (list 0 2 3 4))

(check-equal? (n->th 0) "0th")
(check-equal? (n->th 1) "1st")
(check-equal? (n->th 2) "2nd")
(check-equal? (n->th 3) "3rd")
(check-equal? (n->th 4) "4th")
(check-equal? (n->th 5) "5th")
(check-equal? (n->th 10) "10th")
(check-equal? (n->th 11) "11th")
(check-equal? (n->th 12) "12th")
(check-equal? (n->th 13) "13th")
(check-equal? (n->th 14) "14th")
(check-equal? (n->th 15) "15th")
(check-equal? (n->th 20) "20th")
(check-equal? (n->th 21) "21st")
(check-equal? (n->th 22) "22nd")
(check-equal? (n->th 23) "23rd")
(check-equal? (n->th 24) "24th")
(check-equal? (n->th 25) "25th")
(check-equal? (n->th 100) "100th")
(check-equal? (n->th 101) "101st")
(check-equal? (n->th 102) "102nd")
(check-equal? (n->th 103) "103rd")
(check-equal? (n->th 104) "104th")
(check-equal? (n->th 105) "105th")
(check-equal? (n->th 110) "110th")
(check-equal? (n->th 111) "111th")
(check-equal? (n->th 112) "112th")
(check-equal? (n->th 113) "113th")
(check-equal? (n->th 114) "114th")
(check-equal? (n->th 115) "115th")
(check-equal? (n->th 120) "120th")
(check-equal? (n->th 121) "121st")
(check-equal? (n->th 122) "122nd")
(check-equal? (n->th 123) "123rd")
(check-equal? (n->th 124) "124th")
(check-equal? (n->th 125) "125th")
